package GeneticAlgorithm;

public class GeneticAlgorithm {
    private int populationSize;
    private double mutationRate;
    private double crossoverRate;
    private int elitismCount;

    public GeneticAlgorithm
            (int populationSize, double mutationRate, double crossoverRate, int elitismCount) {
        this.populationSize = populationSize;
        this.mutationRate = mutationRate;
        this.crossoverRate = crossoverRate;
        this.elitismCount = elitismCount;
    }

    public Population initPopulation(int chromosomeLength) {
        Population population = new Population(this.populationSize, chromosomeLength);
        return population;
    }

    // 计算个体的适应度
    public double calcFitness(Individual individual) {
        int correctGenes = 0;
        for (int geneIndex = 0; geneIndex < individual.getChromosomeLength();
             geneIndex++) {
            if (individual.getGene(geneIndex) == 1) {
                correctGenes += 1;
            }
        }
        double fitness = (double) correctGenes / individual.getChromosomeLength();
        individual.setFitness(fitness);
        return fitness;
    }

    public void evalPopulation(Population population) {
        double populationFitness = 0;
        for (Individual individual : population.getIndividuals()) {
            populationFitness += calcFitness(individual);
            population.setPopulationFitness(populationFitness);
        }
    }

    public boolean isTerminationConditionMet(Population population) {
        for (Individual individual : population.getIndividuals()) {
            if (individual.getFitness() == 1) {
                return true;
            }
        }
        return false;
    }

    public Individual selectParent(Population population) {
        Individual individuals[] = population.getIndividuals();
        double populationFitness = population.getPopulationFitness();
        double rouletteWheelPosition = Math.random() * populationFitness;
        double spinWheel = 0;
        for (Individual individual : individuals) {
            spinWheel += individual.getFitness();
            if (spinWheel >= rouletteWheelPosition) {
                return individual;
            }
        }
        return individuals[population.size() - 1];
    }

    public Population crossoverPopulation(Population population) {
        // 初始化一个新的种群
        Population newPopulation = new Population(population.size());
        // 遍历旧种群
        for (int populationIndex = 0; populationIndex < population.size();
             populationIndex++) {
            // 拿取未交叉的最好适应度的个体
            Individual parent1 = population.getFittest(populationIndex);
            // 发生变异并且不是精英个体
            if (this.crossoverRate > Math.random() && populationIndex >=
                    this.elitismCount) {
                // 初始化一个后代
                Individual offspring = new Individual(parent1.getChromosomeLength());
                // 选取和亲1发生交叉的亲2个体
                Individual parent2 = selectParent(population);
                for (int geneIndex = 0; geneIndex < parent1.
                        getChromosomeLength(); geneIndex++) {
                    if (0.5 > Math.random()) {
                        offspring.setGene(geneIndex,
                                parent1.getGene(geneIndex));
                    } else {
                        offspring.setGene(geneIndex,
                                parent2.getGene(geneIndex));
                        newPopulation.setIndividual(populationIndex,
                                offspring);
                    }
                }

            } else {
                newPopulation.setIndividual
                        (populationIndex, parent1);
            }
        }
        return newPopulation;
    }

    public Population mutatePopulation(Population population) {
        Population newPopulation = new Population(this.populationSize);
        for (int populationIndex = 0; populationIndex < population.size();
             populationIndex++) {
            Individual individual = population.
                    getFittest(populationIndex);
            for (int geneIndex = 0; geneIndex < individual.
                    getChromosomeLength(); geneIndex++) {
                if (populationIndex >= this.elitismCount) {
                    if (this.mutationRate > Math.random()) {
                        int newGene = 1;
                        if (individual.getGene(geneIndex) == 1) {
                            newGene = 0;
                        }
                        individual.setGene(geneIndex, newGene);
                    }
                }
            }
            newPopulation.setIndividual(populationIndex, individual);
        }
        return newPopulation;
    }
}
